home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
IRIX 6.2 Applications 1996 May
/
SGI IRIX 6.2 Applications 1996 May.iso
/
dist
/
impr_dev.idb
/
usr
/
impressario
/
src
/
libspool
/
SLUtil.c.z
/
SLUtil.c
Wrap
C/C++ Source or Header
|
1996-05-06
|
20KB
|
744 lines
/**************************************************************************
* *
* Copyright (c) 1991 Silicon Graphics, Inc. *
* All Rights Reserved *
* *
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI *
* *
* The copyright notice above does not evidence any actual of intended *
* publication of such source code, and is an unpublished work by Silicon *
* Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is *
* the property of Silicon Graphics, Inc. Any use, duplication or *
* disclosure not specifically authorized by Silicon Graphics is strictly *
* prohibited. *
* *
* RESTRICTED RIGHTS LEGEND: *
* *
* Use, duplication or disclosure by the Government is subject to *
* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in *
* Technical Data and Computer Software clause at DFARS 52.227-7013, *
* and/or in similar or successor clauses in the FAR, DOD or NASA FAR *
* Supplement. Unpublished - rights reserved under the Copyright Laws of *
* the United States. Contractor is SILICON GRAPHICS, INC., 2011 N. *
* Shoreline Blvd., Mountain View, CA 94039-7311 *
**************************************************************************
*
* File: SLUtil.c
*
* Description: This file contains misc. utility functions grlobally
* available to internal SL routines.
*
**************************************************************************/
#ident "$Revision: 1.11 $"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#ifdef sgi
#include <bstring.h>
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>
#include <ctype.h>
#include "spoolI.h"
/* SLExec spooler output buffer globals */
char **_SLspooler_out_buf; /* Output buffer text */
int _SLspooler_nout; /* # lines in output buffer */
int _SLspooler_exit; /* Spooler exit status */
/* External functions needing declarations */
extern int select(int, fd_set*, fd_set*, fd_set*, struct timeval*);
/* Local functions */
static void spooler_buf_init(void);
static void spooler_buf_add(char*);
static int read_ready(int, int);
/**************************************************************************
*
* Function: _SLIsEmpty
*
* Description: Determines if a string is empty or filled only with
* whitespace.
*
* Parameters:
* str (I) - string to test
*
* Return: 0 not empty. 1 if string is empty.
*
**************************************************************************/
int _SLIsEmpty(register const char *str)
{
if (str == NULL)
return 1;
for (; *str; str++)
if (!isspace((int)*str))
return 0;
return 1;
}
/**************************************************************************
*
* Function: _SLSkipSpace
*
* Description: Skips whitespace and returns a pointer to the first
* non-whitespace character or NULL if there is only whitespace
* on the line.
*
* Parameters:
* sbuf (I) - string buffer to parse for whitespace.
*
* Return: Pointer to first non-whitespace character or NULL if the
* line consists only of whitespace characters;
*
**************************************************************************/
char *_SLSkipSpace(char *sbuf)
{
char *sptr = NULL;
while (*sbuf) {
if (!isspace(*sbuf)) {
sptr = sbuf;
break;
}
sbuf++;
}
return sptr;
}
/**************************************************************************
*
* Function: _SLInitPlist
*
* Description: Initializes a printer list.
*
* Parameters:
* plistp (O) - pointer to printer list.
* nump (O) - pointer to number of printers on list.
*
* Return: none
*
**************************************************************************/
void _SLInitPlist(SLPrinterStruct **plistp, int *nump)
{
register int i;
register SLPrinterStruct *ptr;
if (*plistp) {
/*
* Free the storage for each printer. If this becomes a
* performance problem we can go to free-lists.
*/
for (i = 0, ptr = *plistp; i < *nump; i++, ptr++)
_SLInitPentry(ptr);
/*
* Free the list storage
*/
free((char*)*plistp);
*plistp = NULL;
}
*nump = 0;
}
/**************************************************************************
*
* Function: _SLInitPentry
*
* Description: Initializes a printer info entry. All storage alocated int
* the SLPrinterStruct is freed.
*
* Parameters:
* pentry (O) - pointer to printer info structure.
*
* Return: none
*
**************************************************************************/
void _SLInitPentry(SLPrinterStruct *pentry)
{
if (pentry) {
if (pentry->local_name) {
free((char*)pentry->local_name);
pentry->local_name = NULL;
}
if (pentry->formal_name) {
free((char*)pentry->formal_name);
pentry->formal_name = NULL;
}
if (pentry->type) {
free((char*)pentry->type);
pentry->type = NULL;
}
if (pentry->dev) {
free((char*)pentry->dev);
pentry->dev = NULL;
}
if (pentry->remote_host) {
free((char*)pentry->remote_host);
pentry->remote_host = NULL;
}
if (pentry->remote_name) {
free((char*)pentry->remote_name);
pentry->remote_name = NULL;
}
if (pentry->network_type) {
free(pentry->network_type);
pentry->network_type = NULL;
}
}
}
/**************************************************************************
*
* Function: _SLAddPrinter
*
* Description: Adds a printer to the specified printer list
*
* Parameters:
* pinfo (I) - pointer to the printer info struct to add to the list
* plistp (O) - pointer to printer list.
* nump (O) - pointer to number of printers on list.
*
* Return: None
*
**************************************************************************/
void _SLAddPrinter(const SLPrinterStruct *pinfo, SLPrinterStruct **plistp,
int *nump)
{
(*nump)++;
if (*plistp)
/*
* We realloc for each printer we add. Can realloc in chunks
* if this is a performance problem.
*/
*plistp = (SLPrinterStruct*)realloc(*plistp,
(*nump) * sizeof(SLPrinterStruct));
else
*plistp = (SLPrinterStruct*)malloc((*nump) * sizeof(SLPrinterStruct));
(*plistp)[*nump - 1] = *pinfo;
}
/**************************************************************************
*
* Function: _SLInitQueue
*
* Description: Initializes a printer queue.
*
* Parameters:
* queuep (O) - pointer to queue.
* nump (O) - pointer to number of jobs in queue.
*
* Return: none
*
**************************************************************************/
void _SLInitQueue(SLQueueStruct **queuep, int *nump)
{
int i;
SLQueueStruct *qptr;
if (*queuep) {
/*
* Free the storage for each job. If this becomes a
* performance problem we can go to free-lists.
*/
for (i = 0, qptr = *queuep; i < *nump; i++, qptr++)
_SLFreeQueueEntry(qptr);
/*
* Free the list storage
*/
free((char*)*queuep);
*queuep = NULL;
}
*nump = 0;
}
/**************************************************************************
*
* Function: _SLFreeQueueEntry
*
* Description: Frees the storage for a single queue entry
*
* Parameters:
* entry (I) - pointer to the entry whose storage is to be freed
*
* Return: none
*
**************************************************************************/
void _SLFreeQueueEntry(SLQueueStruct *entry)
{
if (entry->job_id)
free((char*)entry->job_id);
if (entry->username)
free((char*)entry->username);
if (entry->title)
free(entry->title);
}
/**************************************************************************
*
* Function: _SLAddQueue
*
* Description: Adds a job to the specified queue
*
* Parameters:
* queuep (O) - pointer to queue
* nump (O) - pointer to number of jobs in queue.
*
* Return: Pointer to newly added queue structure.
*
**************************************************************************/
SLQueueStruct* _SLAddQueue(SLQueueStruct **queuep, int *nump)
{
(*nump)++;
if (*queuep)
/*
* We realloc for each job we add. Can realloc in chunks
* if this is a performance problem.
*/
*queuep = (SLQueueStruct*)realloc(*queuep,
(*nump) * sizeof(SLQueueStruct));
else
*queuep = (SLQueueStruct*)malloc((*nump) * sizeof(SLQueueStruct));
return &(*queuep)[*nump - 1];
}
/**************************************************************************
*
* Function: _SLExec
*
* Description: Executes the specified command and returns the exit
* status. The command output and the number of lines of output
* are stored in globals for use in error reporting and ID
* parsing.
*
* Parameters:
* job_source (I) - specifies the source of a print job. This is
* specified for print job submittal. Otherwise it may be
* set to NULL.
* cmd (I) - command to execute
* timeout_flag (I) - TRUE == use select with a timeout; FALSE ==
* do not use select (i.e. no timeouts).
* timeout_resp (O) - Timeout result. TRUE if timed out, FALSE if
* did not timeout. Pointer can be NULL if
* timeout_flag is not TRUE.
*
* Return: Exit status of the specified command.
*
**************************************************************************/
int _SLExec(SLJobSourceUnion *job_source, char *cmd, int timeout_flag,
int *timeout_resp)
{
char buf[SL_BUFSIZ];
FILE *pptr;
int status;
int ret, ready_ret, pid, read_pipe[2];
int write_pipe[2];
int infd;
struct sigaction sact, orig_sact;
sigset_t sig_mask;
/*
* First free the old output buffer if any
*/
spooler_buf_init();
/*
* Initialize the write pipe
*/
write_pipe[0] = write_pipe[1] = 0;
/*
* Initialize the input file descriptor. If this is non-zero in
* the child process, we'll dup it to standard input.
*/
infd = 0;
/*
* If appropriate specify a file descriptor for the command to read
* from on its standard input.
*
* If a file descriptor is specified simply tell the command to read
* from that descriptor.
*
* If a buffer has been specified we will open a pipe and specify
* the pipe's read end as the file descriptor for the command to
* read as its stdin. We will write the buffer to the write end of
* the pipe.
*/
(void)strcpy(buf, cmd);
if (job_source) {
switch(job_source->type) {
case SL_JOB_FILENAME:
if (SLdebug)
(void)fprintf(stderr,
gettxt(_SGI_LIBSPOOL_DEBUG_FILENAME_JOB,
"libspool Debug: _SLExec filename job\n"));
break;
case SL_JOB_FD:
if (SLdebug)
(void)fprintf(stderr,
gettxt(_SGI_LIBSPOOL_DEBUG_FD_JOB,
"libspool Debug: _SLExec fd job\n"));
infd = job_source->fd_job.file_desc;
break;
case SL_JOB_BUF:
if (SLdebug)
(void)fprintf(stderr,
gettxt(_SGI_LIBSPOOL_DEBUG_BUF_JOB,
"libspool Debug: _SLExec buffer job\n"));
if (pipe(write_pipe) < 0) {
(void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_PIPE_FAILED,
"libspool INTERNAL ERROR: _SLExec pipe failed (errno=%d)\n"),
errno);
return SL_ERROR;
}
infd = write_pipe[0];
break;
default:
break;
}
}
/*
* Print some debug info if requested
*/
if (SLdebug)
(void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_DEBUG_EXECCMD,
"libspool Debug: _SLExec command \"%s\"\n"), cmd);
/*
* Before we execute the command we need to default SIGCLD so that
* when the command terminates we don not call a user's SIGCLD
* handler. Do not worry about losing SIGCLD for the user because
* if we get one during our ignore period, the user's child will
* zombie and sigaction will raise a new SIGCLD if it detects a
* zombie when we reinstall the original handler.
*/
sigemptyset(&sig_mask);
sact.sa_handler = SIG_DFL;
sact.sa_mask = sig_mask;
sact.sa_flags = 0;
(void)sigaction(SIGCLD, &sact, &orig_sact);
/*
* Execute the command (adapted from popen)
*
* We fork a shell that executes the speicified command. The standard
* out of the command has been connected to the write end of a pipe
* that our parent process reads. For convenience we fdopen a stream
* based on the read end of the pipe.
*/
if (pipe(read_pipe) < 0) {
(void)sigaction(SIGCLD, &orig_sact, NULL);
(void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_PIPE_FAILED,
"libspool INTERNAL ERROR: _SLExec pipe failed (errno=%d)\n"),
errno);
return SL_ERROR;
}
if ((pid = fork()) == 0) {
/*
* Make child a process group leader so that if we timeout
* we can kill the child's pgroup and have all its children killed.
* This is especially important when dealing with rsh since it is
* flakey.
*/
(void)setpgrp();
/*
* Set up the pipes that the child will use to communicate with
* the parent.
*/
if (infd) {
(void)dup2(infd, 0);
(void)close(infd);
}
if (write_pipe[1]) /* If writing to command */
(void)close(write_pipe[1]); /* close child write end */
(void)close(read_pipe[0]); /* Close child read end */
if (read_pipe[1] != 1) { /* If pipe not already stdout */
(void)close(1); /* Close child stdout */
(void)fcntl(read_pipe[1], F_DUPFD, 1); /* Make pipe chld out */
(void)close(read_pipe[1]); /* Close our pipe write */
}
/*
* Set up the environment so that we get output in the
* language we can parse and in the error message format
* we require.
*/
putenv(SL_LANG);
putenv(SL_MSGFORMAT);
/*
* Execute the child command
*/
(void)execl(SL_CMD_SH, SL_CMD_SH_NAME, SL_CMD_SH_FLAG, buf, NULL);
exit(1);
}
if (pid == -1) {
(void)sigaction(SIGCLD, &orig_sact, NULL);
(void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_FORK_FAILED,
"libspool INTERNAL ERROR: _SLExec fork failed (errno=%d)\n"),
errno);
return SL_ERROR;
}
/*
* Set up the pipes that will be used to communicate with
* the child
*/
if (write_pipe[0]) /* If writing to command */
(void)close(write_pipe[0]); /* close parent read end */
(void)close(read_pipe[1]); /* Close parent write end */
/* Create a stream for the pipe */
if ((pptr = fdopen(read_pipe[0], "r")) == NULL) {
(void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_FDOPEN_FAILED,
"libspool INTERNAL ERROR: fdopen failed\n"));
return SL_ERROR;
}
/*
* If the command is to be fed from a buffer, feed it
*/
if (write_pipe[1]) {
(void)write(write_pipe[1], job_source->buf_job.buffer,
job_source->buf_job.amount);
(void)close(write_pipe[1]);
}
/*
* Read the output from the command and build the output buffer
*/
while ((ready_ret = read_ready(read_pipe[0], timeout_flag)) != 0) {
if (fgets(buf, SL_SML_BUFSIZ, pptr) == NULL)
break;
buf[strlen(buf)-1] = '\0';
spooler_buf_add(buf);
}
/*
* Close our end of the pipe to the child
*/
(void)fclose(pptr);
/*
* If the we have a timeout condition, explicitly kill the
* child's process group and set the timeout response
* (if not NULL pointer). The child is a process group leader
* and killing its group kills any children that may have been
* forked.
*/
if (ready_ret == 0) {
if (SLdebug)
(void)fprintf(stderr, gettxt(_SGI_LIBSPOOL_DEBUG_TIMEOUT,
"libspool Debug: _SLExec timeout - killing pid %d\n"), pid);
(void)kill(-pid, SIGKILL);
}
if (timeout_resp)
*timeout_resp = (ready_ret) ? SL_FALSE: SL_TRUE;
/*
* Wait for the child process executing the command to finish
* and save the command exit status. This is adapted from pclose.
*/
#ifdef sgi
while (((ret = waitpid(pid, &status, 0)) < 0) && oserror() == EINTR)
;
#else
while (((ret = waitpid(pid, &status, 0)) < 0) && errno == EINTR)
;
#endif
_SLspooler_exit = ((ret < 0) || (ready_ret == 0)) ?
SL_ERROR : WEXITSTATUS(status);
/*
* Restore the original SIGCLD handler. If there are zombies
* out there a SIGCLD will be raised and the user's desired action
* will be performed.
*/
(void)sigaction(SIGCLD, &orig_sact, NULL);
/*
* If the command exit code was not 0 but we do not have any
* spooler error message, put some generic message in the output
* buffer so that apps get some sort of error text.
*/
if (_SLspooler_exit != 0 && _SLspooler_nout == 0) {
spooler_buf_add(gettxt(_SGI_LIBSPOOL_SPOOLER_CMD_ERR_1,
"Spooler command execution error."));
spooler_buf_add(gettxt(_SGI_LIBSPOOL_SPOOLER_CMD_ERR_2,
"Filenames or other input data may"));
spooler_buf_add(gettxt(_SGI_LIBSPOOL_SPOOLER_CMD_ERR_3,
"be improperly specified."));
}
return _SLspooler_exit;
}
/*
========================================================================
LOCAL FUNCTIONS
========================================================================
*/
/**************************************************************************
*
* Function: spooler_buf_init
*
* Description: Initializes the spooler output message buffer global
* variables.
*
* Parameters: none
*
* Return: none
*
**************************************************************************/
static void spooler_buf_init(void)
{
register int i;
if (_SLspooler_nout) {
for (i = 0; i < _SLspooler_nout; i++)
free((char*)_SLspooler_out_buf[i]);
free((char*)_SLspooler_out_buf);
_SLspooler_out_buf = NULL;
_SLspooler_nout = 0;
}
}
/**************************************************************************
*
* Function: spooler_buf_add
*
* Description: Adds the specified text line to the spooler output message
* buffer.
*
* Parameters:
* str (I) - line of text to add to buffer
*
* Return: none
*
**************************************************************************/
static void spooler_buf_add(char *str)
{
_SLspooler_nout++;
if (_SLspooler_out_buf)
_SLspooler_out_buf = (char**)realloc((char*)_SLspooler_out_buf,
_SLspooler_nout * sizeof(char*));
else
_SLspooler_out_buf = (char**)malloc(sizeof(char*));
_SLspooler_out_buf[_SLspooler_nout-1] = strdup(str);
}
/**************************************************************************
*
* Function: read_ready
*
* Description: Does a select on the read end of the child process output
* pipe. The select will timeout if there is some problem (e.g.
* rsh hanging) or will return normally indicating we have output
* ready to read.
*
* Parameters:
* read_fd (I) - fd to select on for read
* do_select (I) - TRUE == do the timed select. FALSE == simply
* return a ready indication.
*
* Return: 1 is returned if we are ready to read. 0 is returned if select
* has timed out.
*
**************************************************************************/
static int read_ready(int read_fd, int do_select)
{
fd_set fdset;
struct timeval timeout;
int ret, rv = 0, again = 1;
/*
* Return immediately with a ready if select not requested
*/
if (do_select == SL_FALSE)
return 1;
/*
* Run select in a loop in case the call is interrupted by a
* signal (EINTR). This way we can reissue the select.
*/
while (again) {
/*
* Clear the descriptor array and set our descriptor
* for select
*/
FD_ZERO(&fdset);
FD_SET(read_fd, &fdset);
/*
* Set a timeout. Note that we do this in the loop per
* select man page recommendation (see BUGS section in
* man page).
*/
timeout.tv_sec = SLnet_timeout;
timeout.tv_usec = 0;
/*
* Issue the select and handle the response. If a timeout
* occurs return with 0. If select returns normally, return
* a 1. If select is interrupted, don't return but do
* another select. If select returns with any other error
* call it a draw and return 0.
*/
ret = select(read_fd + 1, &fdset, (fd_set*)NULL,
(fd_set*)NULL, &timeout);
again = 0;
if (ret < 0 && errno == EINTR)
again = 1;
else if (ret > 0)
rv = 1;
}
return rv;
}